#include <stdio.h>

#include "common.h"
#include "receive.h"
#include "error.h"
#include "drivers/cc2420.h"
#include "drivers/dbg_uart.h"

uint8_t wait_ack;
uint8_t wait_ack_dsn;
uint8_t psdu_len;
uint8_t psdu_len_sem;

void recv_process(struct comm_unit * cmd)
{
	switch (cmd->cmd) {
		// (uint16_t)
		case OTPLATRADIOSETPANID : {
			cc2420_ram_write_msb(CC2420RAM_PANID, cmd->data, cmd->size);
			break;
		}

		// (uint8_t)[8]
		case OTPLATRADIOSETEXTENDEDADDRESS : {
			cc2420_ram_write_msb(CC2420RAM_IEEEADDR, cmd->data, cmd->size);
			break;
		}

		// (uint16_t)
		case OTPLATRADIOSETSHORTADDRESS : {
			cc2420_ram_write_msb(CC2420RAM_SHORTADDR, cmd->data, cmd->size);
			break;
		}

		// 0
		case OTPLATRADIOGETSTATE : {
			uint8_t fsm_state = cc2420_reg_read(CC2420_FSMSTATE);
			uart_send_comm(&fsm_state, 1);
			break;
		}

		// 0
		case OTPLATRADIOENABLE : {
			cc2420_sxoscon();
			break;
		}

		// 0
		case OTPLATRADIODISABLE : {
			cc2420_sxoscoff();
			break;
		}

		// not implemented
		case OTPLATRADIOISENABLED : {
			// TODO
			break;
		}

		// 0
		case OTPLATRADIOSLEEP : {
			cc2420_srfoff();
			break;
		}

		// (uint8_t)
		case OTPLATRADIORECEIVE : {
			uint16_t f = 357 + 5*(cmd->data[0]-11);

			wait_ack = 0;
			wait_ack_dsn = 0;

			cc2420_reg_write(CC2420_FSCTRL, (0x8000 | (f & 0x03FF)));
			cc2420_srxon();
			break;
		}

		// (uint8_t)
		//case OTPLATRADIOENABLESRCMATCH : {

		// (uint16_t)
		//case OTPLATRADIOADDSRCMATCHSHORTENTRY : {

		// (uint8_t)[8]
		//case OTPLATRADIOADDSRCMATCHEXTENTRY : {

		// (uint16_t)
		//case OTPLATRADIOCLEARSRCMATCHSHORTENTRY : {

		// (uint8_t)[8]
		//case OTPLATRADIOCLEARSRCMATCHEXTENTRY : {

		// 0
		//case OTPLATRADIOCLEARSRCMATCHSHORTENTRIES : {

		// 0
		//case OTPLATRADIOCLEARSRCMATCHEXTENTRIES : {

		// 0
		// expect : uint8_t
		case OTPLATRADIOGETRSSI : {
			break;
		}

		// not implemented (yet)
		case OTPLATRADIOGETTRANSMITPOWER : {
			break;
		}

		// not implemented (yet)
		case OTPLATRADIOSETTRANSMITPOWER : {
			break;
		}

		// 0
		// expect uint8_t
		case OTPLATRADIOGETPROMISCUOUS : {
			break;
		}

		// (uint8_t)
		case OTPLATRADIOSETPROMISCUOUS : {
			uint16_t r;
			r = cc2420_reg_read(CC2420_MDMCTRL0);

			if (cmd->data[0] == 1) {
				r |= BV(11);
			} else {
				r &= ~BV(11);
			}

			cc2420_reg_write(CC2420_MDMCTRL0, r);
			break;
		}

		// not implemented (yet)
		case OTPLATRADIOENERGYSCAN : {
			break;
		}

		// expect : (uint8_t)[128]
		case RADIOPOLLFRAME : {
			uint8_t frame[128];
			uint8_t len = cc2420_get_frame(&(frame[0]));

			if (len > 0)
			{
				/* Skip the checksum */
				uart_send_comm(&(frame[1]), len);
			}
			else
			{
				uart_send_comm(NULL, 0);
			}
			break;
		}

		// (uint8_t)
		case RADIOSETCHANNEL : {
			uint16_t f = 357 + 5*(cmd->data[0]-11);
			cc2420_reg_write(CC2420_FSCTRL, (0x8000 | (f & 0x03FF)));
			break;
		}

		// (uint8_t)
		case RADIOSETPSDULEN : {
			if (psdu_len_sem == 0) {
				psdu_len = cmd->data[0];
				psdu_len_sem = 1;
			} else {
				error("psdulen already set.\n");
			}
			break;
		}

		// (uint8_t)[127-2]
		case RADIOSETPSDU : {
			if (psdu_len_sem == 1)
			{
				psdu_len_sem = 0;
				cc2420_write_txfifo(psdu_len, &(cmd->data[0]));
				cc2420_stxon();

				wait_ack = frame_is_ack_req( &(cmd->data[0]) );
				wait_ack_dsn = frame_get_dsn( &(cmd->data[0]) );
			} else {
				error("No psdulen set.\n");
			}
			break;
		}
	}
}
